package cn.demoncat.util.io;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

import cn.demoncat.util.exception.IoRuntimeException;
import cn.demoncat.util.exception.ParamRuntimeException;

/**
 * NIO处理
 * 
 * @author 延晓磊
 *
 * @since 2018年12月16日
 */
public final class NioUtil {

	private static final String EXP_RW = "NIO通道读写失败：";
	
	/**
	 * 复制文件流
	 * 
	 * @param in	输入流（已关闭）
	 * @param out	输出流（已关闭）
	 */
	public static void copy(FileInputStream in, FileOutputStream out) {
		// 参数校验
		if (in == null || out == null) {
			throw ParamRuntimeException.NULL;
		}
		// 获取文件读取通道
		FileChannel inChannel = in.getChannel();
		// 获取文件输出通道
		FileChannel outChannel = out.getChannel();
		// 创建字节缓冲区
		ByteBuffer buf = ByteBuffer.allocate(FileSizeUtil.autoSize(in));
		// 使用通道传输文件
		try {
			while (true) {
				// 清空缓冲区，保证缓存区最大利用率
				buf.clear();
				// 读取通道中的数据，写入缓冲区，返回-1表示读完
				if (inChannel.read(buf) == -1) {
					break;
				}
				// 反转缓存，读取最新写入缓冲区的数据
				buf.flip();
				// 读取缓冲区的数据，写入通道
				outChannel.write(buf);
			}
		} catch (IOException e) {
			throw new IoRuntimeException(EXP_RW + e.getMessage(), e);
		} finally {
			// 释放资源
			CloseUtil.close(in);
			CloseUtil.close(out);
			CloseUtil.close(inChannel);
			CloseUtil.close(outChannel);
		}
	}

	/**
	 * 读取InputStream到文件流
	 * 
	 * @param in	输入流（已关闭）
	 * @param out	输出流（已关闭）
	 */
	public static void copy(InputStream in, FileOutputStream out) {
		// 参数校验
		if (in == null || out == null) {
			throw ParamRuntimeException.NULL;
		}
		// 获取文件输出通道
		FileChannel outChannel = out.getChannel();
		// 创建字节缓冲区
		int bufSize = FileSizeUtil.autoSize(in);
		ByteBuffer buf = ByteBuffer.allocate(bufSize);
		byte[] bytes = new byte[bufSize];
		int size;
		try {
			// 读取InputStream数据到byte[]
			while ((size = in.read(bytes)) != -1) {
				// 清空缓冲区，保证缓存区最大利用率
				buf.clear();
				// 将byte[]中的数据放到缓存区：0 - 读取的字节数
				buf.put(bytes, 0, size);
				// 反转缓存，读取最新写入缓存的数据
				buf.flip();
				// 读取缓存中的数据，写入通道
				outChannel.write(buf);
			}
		} catch (IOException e) {
			throw new IoRuntimeException(EXP_RW + e.getMessage(), e);
		} finally {
			// 释放资源
			CloseUtil.close(in);
			CloseUtil.close(out);
			CloseUtil.close(outChannel);
		}
	}

	/**
	 * 读取文件流到OutputStream
	 * 
	 * @param in	输入流（已关闭）
	 * @param out	输出流（已关闭）
	 */
	public static void copy(FileInputStream in, OutputStream out) {
		// 参数校验
		if (in == null || out == null) {
			throw ParamRuntimeException.NULL;
		}
		// 获取文件输出通道
		FileChannel inChannel = in.getChannel();
		// 创建字节缓冲区
		int bufSize = FileSizeUtil.autoSize(in);
		ByteBuffer buf = ByteBuffer.allocate(bufSize);
		byte[] bytes = new byte[bufSize];
		int size;
		try {
			while (true) {
				// 清空缓冲区，保证缓存区最大利用率
				buf.clear();
				// 读取通道中的数据，写入缓冲区，返回-1表示读完
				if ((size = inChannel.read(buf)) == -1) {
					break;
				}
				// 反转缓存，读取最新写入缓存的数据
				buf.flip();
				// 将缓存区中的数据读到byte[]：0 - 读取的字节数
				buf.get(bytes, 0, size);
				// 将byte[]中的数据通过OutputStream输出
				out.write(bytes, 0, size);
			}
		} catch (IOException e) {
			throw new IoRuntimeException(EXP_RW + e.getMessage(), e);
		} finally {
			// 释放资源
			CloseUtil.close(in);
			CloseUtil.close(inChannel);
			CloseUtil.close(out);
		}
	}

}
